package com.zwjstudy.cachestudy.jetcache.service.impl;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheManager;
import com.alicp.jetcache.anno.*;
import com.zwjstudy.cachestudy.jetcache.entity.User;
import com.zwjstudy.cachestudy.jetcache.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * ClassName: UserServieImpl
 * Package: com.zwjstudy.cachestudy.springcache.caffeine.service.impl
 * Description:
 *
 * 在分布式集群模式下：
 * 所有的更新操作，全部需要使用分布式锁
 *
 * @Author: 张文俊
 * @Create: 2025-09-20 - 9:43
 * @Version: v1.0
 */
@Service
public class UserServieImpl implements UserService {

    @Autowired
    private CacheManager cacheManager;

    /**
     *
     * 新增后，可以不用管。
     * 因为新增后就是新的数据，当用户在缓存中查询不到会从数据库中查询对应的这个新增数据的。
     * 如果缓存，就是缓存一个整体数据的集合，那么对应这个集合，如果新增了数据，那么处理方式，就是让那个对应这个集合的缓存数据失效即可。
     * @param user
     * @return
     */
    // 新增用户后，失效整个用户列表的缓存
    @CacheInvalidate(area = "userCache", name = "userList:", key = "'all'") // 失效特定的key
    public User save(User user){

        if (user == null) {
            // 记录日志或抛出有意义的业务异常，而不是等到SpEL解析时报错
            throw new IllegalArgumentException("User parameter cannot be null");
        }

        //存储user
        System.out.println("存储用户："+user);
        return user;
    }

    /**
     * 双写模式：
     * 它假设缓存已经被 @Cached创建好了，它的职责只是在数据发生变化时，去更新那个已经存在的缓存中的值。
     * 如果缓存根本不存在（即从未被 @Cached方法创建或已被失效），@CacheUpdate也就无事可做了
     * @param user
     * @return
     */
    @CacheUpdate(area = "userCache",
            name = "userCache:",
            key = "#user.id",
            value = "#user",
            condition = "#user != null && #user.id != null"
    )
    //@Transactional   //解决缓存一致性问题
    public User update(User user){

        if (user == null) {
            // 记录日志或抛出有意义的业务异常，而不是等到SpEL解析时报错
            throw new IllegalArgumentException("User parameter cannot be null");
        }

        //更新user
        System.out.println("更新用户："+user);
        return user;
    }

    /**
     * 推荐：失效模式：
     * 先更新数据库，然后失效缓存（失效模式）
     * @return
     */
    //@CacheInvalidate(area = "userCache", name = "userCache:", key = "#id") // 方法执行成功后，清除指定key的缓存
    //@Transactional   //解决缓存一致性问题
//    public User update(User user){
//        //更新user
//        System.out.println("更新用户："+user);
//        return user;
//    }


    @CacheInvalidate(area = "userCache",
            name = "userCache:",
            key = "#id",
            condition = "#id != null"
    )
    //@Transactional   //解决缓存一致性问题
    public void deleteById(Long id){

        if (id == null) {
            // 记录日志或抛出有意义的业务异常，而不是等到SpEL解析时报错
            throw new IllegalArgumentException("User parameter cannot be null");
        }

        System.out.println("删除用户："+id);

    }

    /**
     * 失效指定缓存区域下的所有键
     * @param area 缓存区域，如 "default"
     * @param name 缓存名称，如 "userCache:"
     */
    public void invalidateAll(String area, String name) {
        // 通过 area 和 name 获取缓存实例
        // 格式通常是 "area@name"，这是 JetCache 内部的命名规则
//        Cache cache = cacheManager.getCache(area + "@" + name);
        Cache cache = cacheManager.getCache(area ,name);

        if (cache != null) {
            //通过redis查询所有的key
            Set<String>  keys = new HashSet<>();

            // 调用 removeAll 方法清除所有键
            cache.removeAll(keys);
        }


    }

    /**
     * 删除所有
     * 对于两级缓存，两种解决方案：
     * 1.直接操作redis，直接清空redis指定key的所有数据
     * 2.通过redis获取指定名称下的所有key，然后通过cacheManager获取对应的cache删除指定的keys
     */
    //@Transactional   //解决缓存一致性问题
    public void deleteAll(){

        System.out.println("删除所有用户");
        invalidateAll("userCache", "userCache:");
    }


    // 使用 @Cached 注解并指定 cacheType 为 BOTH
    @Cached(area = "userCache",
            name = "userCache:",
            key = "#id",
            //expire = 600,
            cacheNullValue = true,//防止缓存穿透
            condition = "#id != null",
            cacheType = CacheType.BOTH
    )
    @CachePenetrationProtect // 添加此注解启用本地锁保护，防止缓存击穿
    public User getById(Long id){

        if (id == null) {
            // 记录日志或抛出有意义的业务异常，而不是等到SpEL解析时报错
            throw new IllegalArgumentException("User parameter cannot be null");
        }

        int i = new Random().nextInt(100);

        User user=new User(id,"haha","h address"+i);

        System.out.println("加载最新的用户数据到缓存中:"+user);

        return user;
    }

    // 查询用户列表的方法（已被缓存）
    @Cached(area = "userCache",
            name = "userList:",
            key = "'all'",
            expire = 30,
            localExpire = 3,
            timeUnit = TimeUnit.SECONDS,
            localLimit =20,
            cacheNullValue = true,//防止缓存穿透
            cacheType=CacheType.BOTH)
    @CacheRefresh(refresh = 15,
            timeUnit = TimeUnit.SECONDS,
            stopRefreshAfterLastAccess = 3600,
            refreshLockTimeout = 30 // ⭐️ 关键设置：分布式锁的超时时间为30秒
    )
    @CachePenetrationProtect // 添加此注解启用本地锁保护，防止缓存击穿
    public List<User> getAllUsers() {
        // 从数据库查询所有用户

        List<User> users =new ArrayList<>();
        users.addAll(Arrays.asList(new User(1,"zs","beijign"),new User(2,"ls","nanjing"),new User(3,"wangwu","shanghai") ));


        System.out.println("加载所有用户");

        return users;
    }

}
